package com.xcjw.com.Hy;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Build;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import com.hyphenate.EMCallBack;
import com.hyphenate.EMConferenceListener;
import com.hyphenate.EMConnectionListener;
import com.hyphenate.EMContactListener;
import com.hyphenate.EMError;
import com.hyphenate.EMGroupChangeListener;
import com.hyphenate.EMMessageListener;
import com.hyphenate.EMMultiDeviceListener;
import com.hyphenate.EMValueCallBack;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMCmdMessageBody;
import com.hyphenate.chat.EMConferenceManager;
import com.hyphenate.chat.EMConferenceMember;
import com.hyphenate.chat.EMConferenceStream;
import com.hyphenate.chat.EMGroup;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMMessage.ChatType;
import com.hyphenate.chat.EMMessage.Status;
import com.hyphenate.chat.EMMessage.Type;
import com.hyphenate.chat.EMMucSharedFile;
import com.hyphenate.chat.EMOptions;
import com.hyphenate.chat.EMStreamStatistics;
import com.hyphenate.chat.EMTextMessageBody;
import com.hyphenate.easeui.EaseUI;
import com.hyphenate.easeui.domain.EaseAvatarOptions;
import com.hyphenate.easeui.domain.EaseEmojicon;
import com.hyphenate.easeui.domain.EaseEmojiconGroupEntity;
import com.hyphenate.easeui.domain.EaseUser;
import com.hyphenate.easeui.model.EaseAtMessageHelper;
import com.hyphenate.easeui.model.EaseNotifier;
import com.hyphenate.easeui.utils.EaseCommonUtils;
import com.hyphenate.exceptions.HyphenateException;
import com.hyphenate.util.EMLog;
import com.hyphenate.util.EasyUtils;
import com.xcjw.com.Base.Myapplication;
import com.xcjw.com.CallBack.CallBackUtils;
import com.xcjw.com.MainActivity;
import com.xcjw.com.R;
import com.xcjw.com.Utils.PreferenceManager;
import com.xcjw.com.cache.UserCacheManager;
import com.xcjw.com.conference.ConferenceActivity;
import com.xcjw.com.conference.LiveActivity;
import com.xcjw.com.db.DemoDBManager;
import com.xcjw.com.db.InviteMessgeDao;
import com.xcjw.com.db.UserDao;
import com.xcjw.com.domain.EmojiconExampleGroupData;
import com.xcjw.com.domain.InviteMessage;
import com.xcjw.com.domain.RobotUser;
import com.xcjw.com.parse.UserProfileManager;
import com.xcjw.com.receiver.CallReceiver;
import com.xcjw.com.receiver.HeadsetReceiver;
import com.xcjw.com.ui.ChatActivity;
import com.xcjw.com.ui.VideoCallActivity;
import com.xcjw.com.ui.VoiceCallActivity;

import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DemoHelper {

      private UserProfileManager userProManager;

      /**
       * data sync listener
       */
      public interface DataSyncListener {
            /**
             * sync complete
             *
             * @param success true：data sync successful，false: failed to sync data
             */
            void onSyncComplete(boolean success);
      }

      protected static final String TAG = "DemoHelper";

      private EaseUI easeUI;

      /**
       * EMEventListener
       */
      protected EMMessageListener messageListener = null;

      private Map<String, EaseUser> contactList;

      private Map<String, RobotUser> robotList;


      private static DemoHelper instance = null;

      private DemoModel demoModel = null;

      /**
       * sync groups status listener
       */
      private List<DataSyncListener> syncGroupsListeners;
      /**
       * sync contacts status listener
       */
      private List<DataSyncListener> syncContactsListeners;
      /**
       * sync blacklist status listener
       */
      private List<DataSyncListener> syncBlackListListeners;

      private boolean isSyncingGroupsWithServer = false;
      private boolean isSyncingContactsWithServer = false;
      private boolean isSyncingBlackListWithServer = false;
      private boolean isGroupsSyncedWithServer = false;
      private boolean isContactsSyncedWithServer = false;
      private boolean isBlackListSyncedWithServer = false;

      public boolean isVoiceCalling;
      public boolean isVideoCalling;

      private String username;

      private Context appContext;

      private CallReceiver callReceiver;

      private InviteMessgeDao inviteMessgeDao;
      private UserDao userDao;

      private LocalBroadcastManager broadcastManager;

      private boolean isGroupAndContactListenerRegisted;

      private ExecutorService executor;

      protected android.os.Handler handler;

      Queue<String> msgQueue = new ConcurrentLinkedQueue<>();

      private DemoHelper() {
            executor = Executors.newCachedThreadPool();
      }

      public synchronized static DemoHelper getInstance() {
            if (instance == null) {
                  instance = new DemoHelper();
            }
            return instance;
      }

      public void execute(Runnable runnable) {
            executor.execute(runnable);
      }

      /**
       * init helper
       *
       * @param context application context
       */
      public void init(Context context) {
            demoModel = new DemoModel(context);
            EMOptions options = initChatOptions();
            options.setAutoLogin(true);
            options.setAcceptInvitationAlways(false);
            initHandler(Looper.myLooper());
//        options.setRestServer("118.193.28.212:31080");
//        options.setIMServer("118.193.28.212");
//        options.setImPort(31097);

            //use default options if options is null
            if (EaseUI.getInstance().init(context, options)) {
                  appContext = context;
                  //debug mode, you'd better set it to false, if you want release your App officially.
                  EMClient.getInstance().setDebugMode(true);
                  //get easeui instance
                  easeUI = EaseUI.getInstance();
                  //to set user's profile and avatar
                  setEaseUIProviders();
                  //initialize preference manager
                  PreferenceManager.init(context);
                  //initialize profile manager
                  getUserProfileManager().init(context);
                  //set Call options
                  setCallOptions();
                  setGlobalListeners();
                  broadcastManager = LocalBroadcastManager.getInstance(appContext);
                  initDbDao();

            }
      }


      private EMOptions initChatOptions() {
            Log.d(TAG, "init HuanXin Options");
            String appid = "2882303761517900294";
            String appkey = "5771790027294";
            EMOptions options = new EMOptions();
            // set if accept the invitation automatically
            options.setAcceptInvitationAlways(false);
            // set if you need read ack
            options.setRequireAck(true);
            // set if you need delivery ack
            options.setRequireDeliveryAck(false);
            options.setAutoLogin(true);
            /**
             * NOTE:你需要设置自己申请的Sender ID来使用Google推送功能，详见集成文档
             */
            // options.setFCMNumber("921300338324");
            //you need apply & set your own id if you want to use Mi push notification
         options.setMipushConfig(appid, appkey);
            // 设置是否使用 fcm，有些华为设备本身带有 google 服务，
            // options.setUseFCM(demoModel.isUseFCM());
            //set custom servers, commonly used in private deployment
            if (demoModel.isCustomServerEnable() && demoModel.getRestServer() != null && demoModel.getIMServer() != null) {
                  options.setRestServer(demoModel.getRestServer());
                  options.setIMServer(demoModel.getIMServer());
                  if (demoModel.getIMServer().contains(":")) {
                        options.setIMServer(demoModel.getIMServer().split(":")[0]);
                        options.setImPort(Integer.valueOf(demoModel.getIMServer().split(":")[1]));
                  }
            }

            if (demoModel.isCustomAppkeyEnabled() && demoModel.getCutomAppkey() != null && !demoModel.getCutomAppkey().isEmpty()) {
                  options.setAppKey(demoModel.getCutomAppkey());
            }

            options.allowChatroomOwnerLeave(getModel().isChatroomOwnerLeaveAllowed());
            options.setDeleteMessagesAsExitGroup(getModel().isDeleteMessagesAsExitGroup());
            options.setAutoAcceptGroupInvitation(getModel().isAutoAcceptGroupInvitation());
            // Whether the message attachment is automatically uploaded to the Hyphenate server,
            options.setAutoTransferMessageAttachments(getModel().isSetTransferFileByUser());
            // Set Whether auto download thumbnail, default value is true.
            options.setAutoDownloadThumbnail(getModel().isSetAutodownloadThumbnail());
            return options;
      }

      private void setCallOptions() {
            HeadsetReceiver headsetReceiver = new HeadsetReceiver();
            IntentFilter headsetFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
            appContext.registerReceiver(headsetReceiver, headsetFilter);

            // min video kbps
            int minBitRate = PreferenceManager.getInstance().getCallMinVideoKbps();
            if (minBitRate != -1) {
                  EMClient.getInstance().callManager().getCallOptions().setMinVideoKbps(minBitRate);
            }

            // max video kbps
            int maxBitRate = PreferenceManager.getInstance().getCallMaxVideoKbps();
            if (maxBitRate != -1) {
                  EMClient.getInstance().callManager().getCallOptions().setMaxVideoKbps(maxBitRate);
            }

            // max frame rate
            int maxFrameRate = PreferenceManager.getInstance().getCallMaxFrameRate();
            if (maxFrameRate != -1) {
                  EMClient.getInstance().callManager().getCallOptions().setMaxVideoFrameRate(maxFrameRate);
            }

            // audio sample rate
            int audioSampleRate = PreferenceManager.getInstance().getCallAudioSampleRate();
            if (audioSampleRate != -1) {
                  EMClient.getInstance().callManager().getCallOptions().setAudioSampleRate(audioSampleRate);
            }

            /**
             * This function is only meaningful when your app need recording
             * If not, remove it.
             * This function need be called before the video stream started, so we set it in onCreate function.
             * This method will set the preferred video record encoding codec.
             * Using default encoding format, recorded file may not be played by mobile player.
             */
            //EMClient.getInstance().callManager().getVideoCallHelper().setPreferMovFormatEnable(true);

            // resolution
            String resolution = PreferenceManager.getInstance().getCallBackCameraResolution();
            if (resolution.equals("")) {
                  resolution = PreferenceManager.getInstance().getCallFrontCameraResolution();
            }
            String[] wh = resolution.split("x");
            if (wh.length == 2) {
                  try {
                        EMClient.getInstance().callManager().getCallOptions().setVideoResolution(new Integer(wh[0]).intValue(), new Integer(wh[1]).intValue());
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
            }

            // enabled fixed sample rate
            boolean enableFixSampleRate = PreferenceManager.getInstance().isCallFixedVideoResolution();
            EMClient.getInstance().callManager().getCallOptions().enableFixedVideoResolution(enableFixSampleRate);

            // Offline call push
            EMClient.getInstance().callManager().getCallOptions().setIsSendPushIfOffline(getModel().isPushCall());
      }

      protected void setEaseUIProviders() {
            //set user avatar to circle shape
            EaseAvatarOptions avatarOptions = new EaseAvatarOptions();
            avatarOptions.setAvatarShape(1);
            easeUI.setAvatarOptions(avatarOptions);

            // set profile provider if you want easeUI to handle avatar and nickname
            easeUI.setUserProfileProvider(new EaseUI.EaseUserProfileProvider() {

                  @Override
                  public EaseUser getUser(String username) {
                        return getUserInfo(username);
                  }
            });

            //set options
            easeUI.setSettingsProvider(new EaseUI.EaseSettingsProvider() {

                  @Override
                  public boolean isSpeakerOpened() {
                        return demoModel.getSettingMsgSpeaker();
                  }

                  @Override
                  public boolean isMsgVibrateAllowed(EMMessage message) {
                        return demoModel.getSettingMsgVibrate();
                  }

                  @Override
                  public boolean isMsgSoundAllowed(EMMessage message) {
                        return demoModel.getSettingMsgSound();
                  }

                  @Override
                  public boolean isMsgNotifyAllowed(EMMessage message) {
                        if (message == null) {
                              return demoModel.getSettingMsgNotification();
                        }
                        if (!demoModel.getSettingMsgNotification()) {
                              return false;
                        } else {
                              String chatUsename = null;
                              List<String> notNotifyIds = null;
                              // get user or group id which was blocked to show message notifications
                              if (message.getChatType() == ChatType.Chat) {
                                    chatUsename = message.getFrom();
                                    notNotifyIds = demoModel.getDisabledIds();
                              } else {
                                    chatUsename = message.getTo();
                                    notNotifyIds = demoModel.getDisabledGroups();
                              }

                              if (notNotifyIds == null || !notNotifyIds.contains(chatUsename)) {
                                    return true;
                              } else {
                                    return false;
                              }
                        }
                  }
            });
            //set emoji icon provider
            easeUI.setEmojiconInfoProvider(new EaseUI.EaseEmojiconInfoProvider() {

                  @Override
                  public EaseEmojicon getEmojiconInfo(String emojiconIdentityCode) {
                        EaseEmojiconGroupEntity data = EmojiconExampleGroupData.getData();
                        for (EaseEmojicon emojicon : data.getEmojiconList()) {
                              if (emojicon.getIdentityCode().equals(emojiconIdentityCode)) {
                                    return emojicon;
                              }
                        }
                        return null;
                  }

                  @Override
                  public Map<String, Object> getTextEmojiconMapping() {
                        return null;
                  }
            });

            //set notification options, will use default if you don't set it
            easeUI.getNotifier().setNotificationInfoProvider(new EaseNotifier.EaseNotificationInfoProvider() {

                  @Override
                  public String getTitle(EMMessage message) {
                        //you can update title here
                        return null;
                  }

                  @Override
                  public int getSmallIcon(EMMessage message) {
                        //you can update icon here
                        return R.mipmap.ic_launcher;
                  }

                  @Override
                  public String getDisplayedText(EMMessage message) {
                        // be used on notification bar, different text according the message type.
                        String ticker = EaseCommonUtils.getMessageDigest(message, appContext);
                        if (message.getType() == Type.TXT) {
                              ticker = ticker.replaceAll("\\[.{2,3}\\]", "[表情]");
                        }
                        EaseUser user = getUserInfo(message.getFrom());
                        if (user != null) {
                              if (EaseAtMessageHelper.get().isAtMeMsg(message)) {
                                    return String.format(appContext.getString(R.string.at_your_in_group), user.getNick());
                              }
                              return user.getNick() + ": " + ticker;
                        } else {
                              if (EaseAtMessageHelper.get().isAtMeMsg(message)) {
                                    return String.format(appContext.getString(R.string.at_your_in_group), message.getFrom());
                              }
                              return message.getFrom() + ": " + ticker;
                        }
                  }

                  @Override
                  public String getLatestText(EMMessage message, int fromUsersNum, int messageNum) {
                        // here you can customize the text.
                        return "收到" + messageNum + "条消息";
                        //return null;
                  }

                  @Override
                  public Intent getLaunchIntent(EMMessage message) {
                        // you can set what activity you want display when user click the notification
                        Intent intent = new Intent(appContext, ChatActivity.class);
                        // open calling activity if there is call
                        if (isVideoCalling) {
                              intent = new Intent(appContext, VideoCallActivity.class);
                        } else if (isVoiceCalling) {
                              intent = new Intent(appContext, VoiceCallActivity.class);
                        } else {
                              ChatType chatType = message.getChatType();
                              if (chatType == ChatType.Chat) { // single chat message
                                    intent.putExtra("userId", message.getFrom());
                                    intent.putExtra("chatType", Constant.CHATTYPE_SINGLE);
                              } else { // group chat message
                                    // message.getTo() is the group id
                                    intent.putExtra("userId", message.getTo());
                                    if (chatType == ChatType.GroupChat) {
                                          intent.putExtra("chatType", Constant.CHATTYPE_GROUP);
                                    } else {
                                          intent.putExtra("chatType", Constant.CHATTYPE_CHATROOM);
                                    }

                              }
                        }
                        return intent;
                  }
            });
      }

      EMConnectionListener connectionListener;

      /**
       * set global listener
       */
      protected void setGlobalListeners() {
            syncGroupsListeners = new ArrayList<>();
            syncContactsListeners = new ArrayList<>();
            syncBlackListListeners = new ArrayList<>();
            isGroupsSyncedWithServer = demoModel.isGroupsSynced();
            isContactsSyncedWithServer = demoModel.isContactSynced();
            isBlackListSyncedWithServer = demoModel.isBacklistSynced();
            // create the global connection listener
            connectionListener = new EMConnectionListener() {
                  @Override
                  public void onDisconnected(int error) {
                        EMLog.d("global listener", "onDisconnect" + error);
                        if (error == EMError.USER_REMOVED) {
                              onUserException(Constant.ACCOUNT_REMOVED);
                        } else if (error == EMError.USER_LOGIN_ANOTHER_DEVICE) {
                              CallBackUtils.doCallBackMethod(6);
                              Log.e(TAG, "onDisconnected: " + "被迫下线");
                        } else if (error == EMError.SERVER_SERVICE_RESTRICTED) {
                              onUserException(Constant.ACCOUNT_FORBIDDEN);
                        } else if (error == EMError.USER_KICKED_BY_CHANGE_PASSWORD) {
                              onUserException(Constant.ACCOUNT_KICKED_BY_CHANGE_PASSWORD);
                        } else if (error == EMError.USER_KICKED_BY_OTHER_DEVICE) {


                              // onUserException(Constant.ACCOUNT_KICKED_BY_OTHER_DEVICE);
                        }
                  }

                  ////  layout.setupWithViewPager(viewpager);
                  @Override
                  public void onConnected() {
                        // in case group and contact were already synced, we supposed to notify sdk we are ready to receive the events
                        if (isGroupsSyncedWithServer && isContactsSyncedWithServer) {
                              EMLog.d(TAG, "group and contact already synced with servre");
                        } else {
                              if (!isGroupsSyncedWithServer) {
                                    asyncFetchGroupsFromServer(null);
                              }

                              if (!isContactsSyncedWithServer) {
                                    asyncFetchContactsFromServer(null);
                              }

                              if (!isBlackListSyncedWithServer) {
                                    asyncFetchBlackListFromServer(null);
                              }
                        }
                  }
            };

            IntentFilter callFilter = new IntentFilter(EMClient.getInstance().callManager().getIncomingCallBroadcastAction());
            if (callReceiver == null) {
                  callReceiver = new CallReceiver();
            }
            EMClient.getInstance().conferenceManager().addConferenceListener(new EMConferenceListener() {
                  @Override
                  public void onMemberJoined(EMConferenceMember member) {
                        EMLog.i(TAG, String.format("member joined username: %s, member: %d", member.memberName,
                                  EMClient.getInstance().conferenceManager().getConferenceMemberList().size()));
                  }

                  @Override
                  public void onMemberExited(EMConferenceMember member) {
                        EMLog.i(TAG, String.format("member exited username: %s, member size: %d", member.memberName,
                                  EMClient.getInstance().conferenceManager().getConferenceMemberList().size()));
                  }

                  @Override
                  public void onStreamAdded(EMConferenceStream stream) {
                        EMLog.i(TAG, String.format("Stream added streamId: %s, streamName: %s, memberName: %s, username: %s, extension: %s, videoOff: %b, mute: %b",
                                  stream.getStreamId(), stream.getStreamName(), stream.getMemberName(), stream.getUsername(),
                                  stream.getExtension(), stream.isVideoOff(), stream.isAudioOff()));
                        EMLog.i(TAG, String.format("Conference stream subscribable: %d, subscribed: %d",
                                  EMClient.getInstance().conferenceManager().getAvailableStreamMap().size(),
                                  EMClient.getInstance().conferenceManager().getSubscribedStreamMap().size()));
                  }

                  @Override
                  public void onStreamRemoved(EMConferenceStream stream) {
                        EMLog.i(TAG, String.format("Stream removed streamId: %s, streamName: %s, memberName: %s, username: %s, extension: %s, videoOff: %b, mute: %b",
                                  stream.getStreamId(), stream.getStreamName(), stream.getMemberName(), stream.getUsername(),
                                  stream.getExtension(), stream.isVideoOff(), stream.isAudioOff()));
                        EMLog.i(TAG, String.format("Conference stream subscribable: %d, subscribed: %d",
                                  EMClient.getInstance().conferenceManager().getAvailableStreamMap().size(),
                                  EMClient.getInstance().conferenceManager().getSubscribedStreamMap().size()));
                  }

                  @Override
                  public void onStreamUpdate(EMConferenceStream stream) {
                        EMLog.i(TAG, String.format("Stream added streamId: %s, streamName: %s, memberName: %s, username: %s, extension: %s, videoOff: %b, mute: %b",
                                  stream.getStreamId(), stream.getStreamName(), stream.getMemberName(), stream.getUsername(),
                                  stream.getExtension(), stream.isVideoOff(), stream.isAudioOff()));
                        EMLog.i(TAG, String.format("Conference stream subscribable: %d, subscribed: %d",
                                  EMClient.getInstance().conferenceManager().getAvailableStreamMap().size(),
                                  EMClient.getInstance().conferenceManager().getSubscribedStreamMap().size()));
                  }

                  @Override
                  public void onPassiveLeave(int error, String message) {
                        EMLog.i(TAG, String.format("passive leave code: %d, message: %s", error, message));
                  }

                  @Override
                  public void onConferenceState(ConferenceState state) {
                        EMLog.i(TAG, String.format("State code=%d", state.ordinal()));
                  }

                  @Override
                  public void onStreamStatistics(EMStreamStatistics statistics) {
                        EMLog.d(TAG, statistics.toString());
                  }

                  @Override
                  public void onStreamSetup(String streamId) {
                        EMLog.i(TAG, String.format("Stream id - %s", streamId));
                  }

                  @Override
                  public void onSpeakers(List<String> speakers) {
                  }

                  @Override
                  public void onReceiveInvite(String confId, String password, String extension) {
                        EMLog.i(TAG, String.format("Receive conference invite confId: %s, password: %s, extension: %s", confId, password, extension));
                        goConference(confId, password, extension);
                  }

                  @Override
                  public void onRoleChanged(EMConferenceManager.EMConferenceRole role) {
                  }
            });
            //register incoming call receiver
            appContext.registerReceiver(callReceiver, callFilter);
            //register connection listener
            EMClient.getInstance().addConnectionListener(connectionListener);
            //register group and contact event listener
            registerGroupAndContactListener();
            //register message event listener
            registerMessageListener();

      }

      @TargetApi(Build.VERSION_CODES.LOLLIPOP)
      private void showiosDialog() {
            AlertDialog.Builder builder = new AlertDialog.Builder(Myapplication.mcontext);
            View view = View
                      .inflate(Myapplication.mcontext, R.layout.login_out, null);
            builder.setView(view);
            builder.setCancelable(true);
            final AlertDialog dialog = builder.create();
            TextView buttonok = (TextView) view.findViewById(R.id.ok);
            buttonok.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                        CallBackUtils.doCallBackMethod(4);

                        dialog.dismiss();
                  }
            });

            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            dialog.getWindow().setGravity(Gravity.CENTER);
            dialog.show();


//        final CustomDialog dialog = new CustomDialog(Myapplication.mcontext, R.style.customDialog, R.layout.ios_dialog2);
//
//        TextView tvContent = (TextView) dialog.findViewById(R.id.textsign);
//        tvContent.setText("另一个账号登录，您被迫下线");
//        TextView tvCancel = (TextView) dialog.findViewById(R.id.tv_cancl);
//        TextView tvOk = (TextView) dialog.findViewById(R.id.tv_bind);
//        tvOk.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View view) {
//                dialog.dismiss();
//            }
//        });
//        tvCancel.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View view) {
//                dialog.dismiss();
//            }
//        });
      }

      /**
       * 处理会议邀请
       *
       * @param confId   会议 id
       * @param password 会议密码
       */
      public void goConference(String confId, String password, String extension) {
            if (isDuringMediaCommunication()) {
                  return;
            }
            String inviter = "";
            String groupId = null;
            try {
                  JSONObject jsonObj = new JSONObject(extension);
                  inviter = jsonObj.optString(Constant.EXTRA_CONFERENCE_INVITER);
                  groupId = jsonObj.optString(Constant.EXTRA_CONFERENCE_GROUP_ID);
            } catch (JSONException e) {
                  e.printStackTrace();
            }

            ConferenceActivity.receiveConferenceCall(appContext, confId, password, inviter, groupId);
      }

      public void goLive(String confId, String password, String inviter) {
            if (isDuringMediaCommunication()) {
                  return;
            }

            LiveActivity.watch(appContext, confId, password, inviter);
      }

      private void initDbDao() {
            inviteMessgeDao = new InviteMessgeDao(appContext);
            userDao = new UserDao(appContext);
      }

      private boolean isDuringMediaCommunication() {
            String topClassName = easeUI.getTopActivity().getClass().getSimpleName();
            if (easeUI.hasForegroundActivies() && ("LiveActivity".equals(topClassName) || "ConferenceActivity".equals(topClassName))) {
                  return true;
            }
            return false;
      }

      /**
       * register group and contact listener, you need register when login
       */
      public void registerGroupAndContactListener() {
            if (!isGroupAndContactListenerRegisted) {
                  EMClient.getInstance().groupManager().addGroupChangeListener(new MyGroupChangeListener());
                  EMClient.getInstance().contactManager().setContactListener(new MyContactListener());
                  EMClient.getInstance().addMultiDeviceListener(new MyMultiDeviceListener());
                  isGroupAndContactListenerRegisted = true;
            }

      }

      /**
       * group change listener
       */
      class MyGroupChangeListener implements EMGroupChangeListener {

            @Override
            public void onInvitationReceived(String groupId, String groupName, String inviter, String reason) {

                  new InviteMessgeDao(appContext).deleteMessage(groupId);

                  // user invite you to join group
                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(groupId);
                  msg.setTime(System.currentTimeMillis());
                  msg.setGroupId(groupId);
                  msg.setGroupName(groupName);

                  msg.setReason(reason);
                  msg.setGroupInviter(inviter);
                  // showToast("receive invitation to join the group：" + groupName);
                  msg.setStatus(InviteMessage.InviteMessageStatus.GROUPINVITATION);
                  notifyNewInviteMessage(msg);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
            }

            @Override
            public void onInvitationAccepted(String groupId, String invitee, String reason) {

                  new InviteMessgeDao(appContext).deleteMessage(groupId);

                  //user accept your invitation
                  boolean hasGroup = false;
                  EMGroup _group = null;
                  for (EMGroup group : EMClient.getInstance().groupManager().getAllGroups()) {
                        if (group.getGroupId().equals(groupId)) {
                              hasGroup = true;
                              _group = group;
                              break;
                        }
                  }
                  if (!hasGroup)
                        return;

                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(groupId);
                  msg.setTime(System.currentTimeMillis());
                  msg.setGroupId(groupId);
                  msg.setGroupName(_group == null ? groupId : _group.getGroupName());
                  msg.setReason(reason);
                  msg.setGroupInviter(invitee);
                  // showToast(invitee + "Accept to join the group：" + _group == null ? groupId : _group.getGroupName());
                  msg.setStatus(InviteMessage.InviteMessageStatus.GROUPINVITATION_ACCEPTED);
                  notifyNewInviteMessage(msg);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
            }

            @Override
            public void onInvitationDeclined(String groupId, String invitee, String reason) {

                  new InviteMessgeDao(appContext).deleteMessage(groupId);

                  //user declined your invitation
                  EMGroup group = null;
                  for (EMGroup _group : EMClient.getInstance().groupManager().getAllGroups()) {
                        if (_group.getGroupId().equals(groupId)) {
                              group = _group;
                              break;
                        }
                  }
                  if (group == null)
                        return;

                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(groupId);
                  msg.setTime(System.currentTimeMillis());
                  msg.setGroupId(groupId);
                  msg.setGroupName(group.getGroupName());
                  msg.setReason(reason);
                  msg.setGroupInviter(invitee);
                  // showToast(invitee + "Declined to join the group：" + group.getGroupName());
                  msg.setStatus(InviteMessage.InviteMessageStatus.GROUPINVITATION_DECLINED);
                  notifyNewInviteMessage(msg);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
            }

            @Override
            public void onUserRemoved(String groupId, String groupName) {
                  //user is removed from group
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
                  //showToast("current user removed, groupId:" + groupId);
            }

            @Override
            public void onGroupDestroyed(String groupId, String groupName) {
                  // group is dismissed,
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
                  showToast("您所在的" + groupName + "已解散");
            }

            @Override
            public void onRequestToJoinReceived(String groupId, String groupName, String applyer, String reason) {

                  // user apply to join group
                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(applyer);
                  msg.setTime(System.currentTimeMillis());
                  msg.setGroupId(groupId);
                  msg.setGroupName(groupName);
                  msg.setReason(reason);
                  //  showToast(applyer + " Apply to join group：" + groupId);
                  msg.setStatus(InviteMessage.InviteMessageStatus.BEAPPLYED);
                  notifyNewInviteMessage(msg);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
            }

            @Override
            public void onRequestToJoinAccepted(String groupId, String groupName, String accepter) {

                  String st4 = appContext.getString(R.string.Agreed_to_your_group_chat_application);
                  // your application was accepted
                  EMMessage msg = EMMessage.createReceiveMessage(Type.TXT);
                  msg.setChatType(ChatType.GroupChat);
                  msg.setFrom(accepter);
                  msg.setTo(groupId);
                  msg.setMsgId(UUID.randomUUID().toString());
                  msg.addBody(new EMTextMessageBody(accepter + " " + st4));
                  msg.setStatus(Status.SUCCESS);
                  // save accept message
                  EMClient.getInstance().chatManager().saveMessage(msg);
                  // notify the accept message
                  getNotifier().vibrateAndPlayTone(msg);

                  showToast(groupName + "接受了您的邀请");
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
            }

            @Override
            public void onRequestToJoinDeclined(String groupId, String groupName, String decliner, String reason) {
                  // your application was declined, we do nothing here in demo
                  showToast(groupName + "拒绝您的入群申请");
            }

            @Override
            public void onAutoAcceptInvitationFromGroup(String groupId, String inviter, String inviteMessage) {
                  // got an invitation
                  String st3 = appContext.getString(R.string.Invite_you_to_join_a_group_chat);
                  EMMessage msg = EMMessage.createReceiveMessage(Type.TXT);
                  msg.setChatType(ChatType.GroupChat);
                  msg.setFrom(inviter);
                  msg.setTo(groupId);
                  msg.setMsgId(UUID.randomUUID().toString());
                  msg.addBody(new EMTextMessageBody(inviter + " " + st3));
                  msg.setStatus(Status.SUCCESS);
                  // save invitation as messages
                  EMClient.getInstance().chatManager().saveMessage(msg);
                  // notify invitation message
                  getNotifier().vibrateAndPlayTone(msg);
                  //showToast("自动接受加入群号为" + groupId + "的群聊");
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
            }

            // ============================= group_reform new add api begin
            @Override
            public void onMuteListAdded(String groupId, final List<String> mutes, final long muteExpire) {
                  StringBuilder sb = new StringBuilder();
                  for (String member : mutes) {
                        sb.append(member).append(",");
                  }
                  //// TODO: 2018/10/26  已加入了群提醒
                  // showToast("onMuterListAdded: " + sb.toString());
            }


            @Override
            public void onMuteListRemoved(String groupId, final List<String> mutes) {
                  StringBuilder sb = new StringBuilder();
                  for (String member : mutes) {
                        sb.append(member).append(",");
                  }
                  // showToast("onMuterListRemoved: " + sb.toString());
            }


            @Override
            public void onAdminAdded(String groupId, String administrator) {
                  //showToast("onAdminAdded: " + administrator);
            }

            @Override
            public void onAdminRemoved(String groupId, String administrator) {
                  //showToast("onAdminRemoved: " + administrator);
            }

            @Override
            public void onOwnerChanged(String groupId, String newOwner, String oldOwner) {
                  // showToast("onOwnerChanged new:" + newOwner + " old:" + oldOwner);
            }

            @Override
            public void onMemberJoined(String groupId, String member) {
                  //showToast("onMemberJoined: " + member);
            }

            @Override
            public void onMemberExited(String groupId, String member) {
                  // showToast("onMemberExited: " + member);
            }

            @Override
            public void onAnnouncementChanged(String groupId, String announcement) {
                  // showToast("onAnnouncementChanged, groupId" + groupId);
            }

            @Override
            public void onSharedFileAdded(String groupId, EMMucSharedFile sharedFile) {
                  // showToast("onSharedFileAdded, groupId" + groupId);
            }

            @Override
            public void onSharedFileDeleted(String groupId, String fileId) {
                  // showToast("onSharedFileDeleted, groupId" + groupId);
            }
            // ============================= group_reform new add api end
      }

      void showToast(final String message) {
            // Log.d(TAG, "receive invitation to join the group：" + message);
            if (handler != null) {
                  Message msg = Message.obtain(handler, 0, message);
                  handler.sendMessage(msg);
            } else {
                  msgQueue.add(message);
            }
      }

      public void initHandler(Looper looper) {
            handler = new android.os.Handler(looper) {
                  @Override
                  public void handleMessage(Message msg) {

                        if (msg.what == 10) {
                              if (EasyUtils.isAppRunningForeground(appContext)) {
                                    appContext.startActivity(new Intent(appContext, MainActivity.class));
                                    if (checkFloatPermission(appContext)) {
                                          showiosDialog();
                                    } else {
                                          CallBackUtils.doCallBackMethod(4);
                                    }
                              } else {
                                    if (checkFloatPermission(appContext)) {
                                          showiosDialog();
                                    } else {
                                          CallBackUtils.doCallBackMethod(4);
                                    }
                              }

                        } else {
                              String str = (String) msg.obj;
                              Toast.makeText(appContext, str, Toast.LENGTH_LONG).show();
                        }
                  }
            };
            while (!msgQueue.isEmpty()) {
                  //   showToast(msgQueue.remove());
            }
      }

      public static boolean checkFloatPermission(Context context) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
                  return true;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                  try {
                        Class cls = Class.forName("android.content.Context");
                        Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE");
                        declaredField.setAccessible(true);
                        Object obj = declaredField.get(cls);
                        if (!(obj instanceof String)) {
                              return false;
                        }
                        String str2 = (String) obj;
                        obj = cls.getMethod("getSystemService", String.class).invoke(context, str2);
                        cls = Class.forName("android.app.AppOpsManager");
                        Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED");
                        declaredField2.setAccessible(true);
                        Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);
                        int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName());
                        return result == declaredField2.getInt(cls);
                  } catch (Exception e) {
                        return false;
                  }
            } else {
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
                        if (appOpsMgr == null)
                              return false;
                        int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), context
                                  .getPackageName());
                        return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED;
                  } else {
                        return Settings.canDrawOverlays(context);
                  }
            }
      }

      /***
       * 好友变化listener
       */
      public class MyContactListener implements EMContactListener {

            @Override
            public void onContactAdded(String username) {
                  // save contact
                  Map<String, EaseUser> localUsers = getContactList();
                  Map<String, EaseUser> toAddUsers = new HashMap<String, EaseUser>();
                  EaseUser user = new EaseUser(username);

                  if (!localUsers.containsKey(username)) {
                        userDao.saveContact(user);
                  }
                  toAddUsers.put(username, user);
                  localUsers.putAll(toAddUsers);

                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
                  //showToast( username +);
            }

            @Override
            public void onContactDeleted(String username) {
                  Map<String, EaseUser> localUsers = DemoHelper.getInstance().getContactList();
                  localUsers.remove(username);
                  userDao.deleteContact(username);
                  inviteMessgeDao.deleteMessage(username);
                  EMClient.getInstance().chatManager().deleteConversation(username, false);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
                  //showToast("onContactDeleted:" + username);
            }

            @Override
            public void onContactInvited(String username, String reason) {
                  List<InviteMessage> msgs = inviteMessgeDao.getMessagesList();

                  for (InviteMessage inviteMessage : msgs) {
                        if (inviteMessage.getGroupId() == null && inviteMessage.getFrom().equals(username)) {
                              inviteMessgeDao.deleteMessage(username);
                        }
                  }
                  // save invitation as message
                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(username);
                  msg.setTime(System.currentTimeMillis());
                  msg.setReason(reason);
                  //showToast(username + "apply to be your friend,reason: " + reason);
                  // set invitation status
                  msg.setStatus(InviteMessage.InviteMessageStatus.BEINVITEED);
                  notifyNewInviteMessage(msg);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
            }

            @Override
            public void onFriendRequestAccepted(String username) {
                  List<InviteMessage> msgs = inviteMessgeDao.getMessagesList();
                  for (InviteMessage inviteMessage : msgs) {
                        if (inviteMessage.getFrom().equals(username)) {
                              return;
                        }
                  }
                  // save invitation as message
                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(username);
                  msg.setTime(System.currentTimeMillis());
                  showToast(username + " 同意了您的好友申请");
                  msg.setStatus(InviteMessage.InviteMessageStatus.BEAGREED);
                  notifyNewInviteMessage(msg);
                  broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
            }

            @Override
            public void onFriendRequestDeclined(String username) {
                  // your request was refused
                  showToast(username + " 拒绝了您的好友申请");
            }
      }

      public class MyMultiDeviceListener implements EMMultiDeviceListener {

            @Override
            public void onContactEvent(int event, String target, String ext) {
                  switch (event) {
                        case EMMultiDeviceListener.CONTACT_REMOVE: {
                              Map<String, EaseUser> localUsers = getContactList();
                              localUsers.remove(target);
                              userDao.deleteContact(target);
                              inviteMessgeDao.deleteMessage(target);
                              EMClient.getInstance().chatManager().deleteConversation(username, false);
                              broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
                              //showToast("CONTACT_REMOVE");
                        }
                        break;
                        case EMMultiDeviceListener.CONTACT_ACCEPT: {
                              Map<String, EaseUser> localUsers = getContactList();
                              EaseUser user = new EaseUser(target);
                              if (!localUsers.containsKey(target)) {
                                    userDao.saveContact(user);
                              }
                              localUsers.put(target, user);
                              updateContactNotificationStatus(target, "", InviteMessage.InviteMessageStatus.MULTI_DEVICE_CONTACT_ACCEPT);
                              broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
                              // showToast("CONTACT_ACCEPT");
                        }
                        break;
                        case EMMultiDeviceListener.CONTACT_DECLINE:
                              updateContactNotificationStatus(target, "", InviteMessage.InviteMessageStatus.MULTI_DEVICE_CONTACT_DECLINE);
                              //showToast("CONTACT_DECLINE");
                              break;
//                case CONTACT_ADD:
//                    updateContactNotificationStatus(target, "", InviteMessage.InviteMessageStatus.MULTI_DEVICE_CONTACT_ADD);
//                    showToast("CONTACT_ADD");
//                break;
                        case CONTACT_BAN:
                              updateContactNotificationStatus(target, "", InviteMessage.InviteMessageStatus.MULTI_DEVICE_CONTACT_BAN);
                              // showToast("CONTACT_BAN");

                              Map<String, EaseUser> localUsers = DemoHelper.getInstance().getContactList();
                              localUsers.remove(username);
                              userDao.deleteContact(username);
                              inviteMessgeDao.deleteMessage(username);
                              EMClient.getInstance().chatManager().deleteConversation(username, false);
                              broadcastManager.sendBroadcast(new Intent(Constant.ACTION_CONTACT_CHANAGED));
                              break;
                        case CONTACT_ALLOW:
                              updateContactNotificationStatus(target, "", InviteMessage.InviteMessageStatus.MULTI_DEVICE_CONTACT_ALLOW);
                              //showToast("CONTACT_ALLOW");
                              break;
                        default:
                              break;
                  }
            }

            private void updateContactNotificationStatus(String from, String reason, InviteMessage.InviteMessageStatus status) {
                  InviteMessage msg = null;
                  for (InviteMessage _msg : inviteMessgeDao.getMessagesList()) {
                        if (_msg.getFrom().equals(from)) {
                              msg = _msg;
                              break;
                        }
                  }
                  if (msg != null) {
                        ContentValues values = new ContentValues();
                        msg.setStatus(status);
                        values.put(InviteMessgeDao.COLUMN_NAME_STATUS, msg.getStatus().ordinal());
                        inviteMessgeDao.updateMessage(msg.getId(), values);
                  } else {
                        // save invitation as message
                        msg = new InviteMessage();
                        msg.setFrom(username);
                        msg.setTime(System.currentTimeMillis());
                        msg.setReason(reason);
                        msg.setStatus(status);
                        notifyNewInviteMessage(msg);
                  }
            }

            @Override
            public void onGroupEvent(final int event, final String target, final List<String> usernames) {
                  execute(new Runnable() {
                        @Override
                        public void run() {
                              try {
                                    String groupId = target;
                                    switch (event) {
                                          case GROUP_CREATE:
                                                //showToast("GROUP_CREATE");
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_CREATE);
                                                break;
                                          case GROUP_DESTROY:
                                                // showToast("GROUP_DESTROY");
                                                inviteMessgeDao.deleteGroupMessage(groupId);
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_DESTROY);
                                                broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
                                                break;
                                          case GROUP_JOIN:
                                                //showToast("GROUP_JOIN");
                                                broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_JOIN);
                                                break;
                                          case GROUP_LEAVE:
                                                //showToast("GROUP_LEAVE");
                                                inviteMessgeDao.deleteGroupMessage(groupId);
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_LEAVE);
                                                broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
                                                break;
                                          case GROUP_APPLY:
                                                // showToast("GROUP_APPLY");
                                                inviteMessgeDao.deleteGroupMessage(groupId);
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_APPLY);
                                                break;
                                          case GROUP_APPLY_ACCEPT:
                                                //showToast("GROUP_ACCEPT");
                                                inviteMessgeDao.deleteGroupMessage(groupId, usernames.get(0));
                                                // TODO: person, reason from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_APPLY_ACCEPT);
                                                break;
                                          case GROUP_APPLY_DECLINE:
                                                // showToast("GROUP_APPLY_DECLINE");
                                                inviteMessgeDao.deleteGroupMessage(groupId, usernames.get(0));
                                                // TODO: person, reason from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_APPLY_DECLINE);
                                                break;
                                          case GROUP_INVITE:
                                                //  showToast("GROUP_INVITE");
                                                // TODO: person, reason from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_INVITE);
                                                break;
                                          case GROUP_INVITE_ACCEPT:
                                                /// showToast("GROUP_INVITE_ACCEPT");
                                                String st3 = appContext.getString(R.string.Invite_you_to_join_a_group_chat);
                                                EMMessage msg = EMMessage.createReceiveMessage(Type.TXT);
                                                msg.setChatType(ChatType.GroupChat);
                                                // TODO: person, reason from ext
                                                String from = "";
                                                if (usernames != null && usernames.size() > 0) {
                                                      msg.setFrom(usernames.get(0));
                                                }
                                                msg.setTo(groupId);
                                                msg.setMsgId(UUID.randomUUID().toString());
                                                msg.addBody(new EMTextMessageBody(msg.getFrom() + " " + st3));
                                                msg.setStatus(Status.SUCCESS);
                                                // save invitation as messages
                                                EMClient.getInstance().chatManager().saveMessage(msg);

                                                inviteMessgeDao.deleteMessage(groupId);
                                                // TODO: person, reason from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_INVITE_ACCEPT);
                                                broadcastManager.sendBroadcast(new Intent(Constant.ACTION_GROUP_CHANAGED));
                                                break;
                                          case GROUP_INVITE_DECLINE:
                                                // showToast("GROUP_INVITE_DECLINE");
                                                inviteMessgeDao.deleteMessage(groupId);
                                                // TODO: person, reason from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_INVITE_DECLINE);
                                                break;
                                          case GROUP_KICK:
                                                // showToast("GROUP_KICK");
                                                // TODO: person, reason from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_INVITE_DECLINE);
                                                break;
                                          case GROUP_BAN:
                                                //  showToast("GROUP_BAN");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_BAN);
                                                break;
                                          case GROUP_ALLOW:
                                                ///showToast("GROUP_ALLOW");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_ALLOW);
                                                break;
                                          case GROUP_BLOCK:
                                                // showToast("GROUP_BLOCK");
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_BLOCK);
                                                break;
                                          case GROUP_UNBLOCK:
                                                //showToast("GROUP_UNBLOCK");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/"", /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_UNBLOCK);
                                                break;
                                          case GROUP_ASSIGN_OWNER:
                                                //showToast("GROUP_ASSIGN_OWNER");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_ASSIGN_OWNER);
                                                break;
                                          case GROUP_ADD_ADMIN:
                                                //  showToast("GROUP_ADD_ADMIN");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_ADD_ADMIN);
                                                break;
                                          case GROUP_REMOVE_ADMIN:
                                                /// showToast("GROUP_REMOVE_ADMIN");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_REMOVE_ADMIN);
                                                break;
                                          case GROUP_ADD_MUTE:
                                                //  showToast("GROUP_ADD_MUTE");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_ADD_MUTE);
                                                break;
                                          case GROUP_REMOVE_MUTE:
                                                // showToast("GROUP_REMOVE_MUTE");
                                                // TODO: person from ext
                                                saveGroupNotification(groupId, /*groupName*/"",  /*person*/usernames.get(0), /*reason*/"", InviteMessage.InviteMessageStatus.MULTI_DEVICE_GROUP_REMOVE_MUTE);
                                                break;
                                          default:
                                                break;
                                    }

                                    if (false) { // keep the try catch structure
                                          throw new HyphenateException("");
                                    }
                              } catch (HyphenateException e) {
                                    e.printStackTrace();
                              }

                        }
                  });
            }

            private void saveGroupNotification(String groupId, String groupName, String inviter, String reason, InviteMessage.InviteMessageStatus status) {
                  InviteMessage msg = new InviteMessage();
                  msg.setFrom(groupId);
                  msg.setTime(System.currentTimeMillis());
                  msg.setGroupId(groupId);
                  msg.setGroupName(groupName);
                  msg.setReason(reason);
                  msg.setGroupInviter(inviter);
                  Log.d(TAG, "receive invitation to join the group：" + groupName);
                  msg.setStatus(status);
                  notifyNewInviteMessage(msg);
            }

            private void updateGroupNotificationStatus(String groupId, String groupName, String inviter, String reason, InviteMessage.InviteMessageStatus status) {
                  InviteMessage msg = null;
                  for (InviteMessage _msg : inviteMessgeDao.getMessagesList()) {
                        if (_msg.getGroupId().equals(groupId)) {
                              msg = _msg;
                              break;
                        }
                  }
                  if (msg != null) {
                        ContentValues values = new ContentValues();
                        msg.setStatus(status);
                        values.put(InviteMessgeDao.COLUMN_NAME_STATUS, msg.getStatus().ordinal());
                        inviteMessgeDao.updateMessage(msg.getId(), values);
                  }
            }
      }

      /**
       * save and notify invitation message
       *
       * @param msg
       */
      private void notifyNewInviteMessage(InviteMessage msg) {
            if (inviteMessgeDao == null) {
                  inviteMessgeDao = new InviteMessgeDao(appContext);
            }
            inviteMessgeDao.saveMessage(msg);
            //increase the unread message count
            inviteMessgeDao.saveUnreadMessageCount(1);
            // notify there is new message
            getNotifier().vibrateAndPlayTone(null);
      }

      /**
       * user met some exception: conflict, removed or forbidden
       */
      protected void onUserException(String exception) {
            EMLog.e(TAG, "onUserException: " + exception);
            Intent intent = new Intent(appContext, MainActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
            intent.putExtra(exception, true);
            appContext.startActivity(intent);

            // showToast(exception);
      }

      private EaseUser getUserInfo(String username) {
            // To get instance of EaseUser, here we get it from the user list in memory
            // You'd better cache it if you get it from your server
//            EaseUser user = null;
//        if(username.equals(EMClient.getInstance().getCurrentUser()))
//            return getUserProfileManager().getCurrentUserInfo();
            EaseUser user = UserCacheManager.getEaseUser(username);
//            user = getContactList().get(username);
//            if (user == null && getRobotList() != null) {
//                  user = getRobotList().get(username);
//            }

            // if user is not in your contacts, set inital letter for him/her
//            if (user == null) {
//                  user = new EaseUser(username);
//                  EaseCommonUtils.setUserInitialLetter(user);
//            }
            return user;
      }

      /**
       * Global listener
       * If this event already handled by an activity, you don't need handle it again
       * activityList.size() <= 0 means all activities already in background or not in Activity Stack
       */
      protected void registerMessageListener() {
            messageListener = new EMMessageListener() {
                  @Override
                  public void onMessageReceived(List<EMMessage> messages) {
                        for (EMMessage message : messages) {
                              EMLog.d(TAG, "onMessageReceived id : " + message.getMsgId());
//                    // 判断一下是否是会议邀请
                              UserCacheManager.save(message.ext());
                              DemoHelper.getInstance().getNotifier().vibrateAndPlayTone(null);

//                    String confId = message.getStringAttribute(Constant.MSG_ATTR_CONF_ID, "");
//                    if(!"".equals(confId)){
//                        String password = message.getStringAttribute(Constant.MSG_ATTR_CONF_PASS, "");
//                        String extension = message.getStringAttribute(Constant.MSG_ATTR_EXTENSION, "");
//                        goConference(confId, password, extension);
//                    }
                              // in background, do not refresh UI, notify it in notification bar
                              if (!easeUI.hasForegroundActivies()) {
                                    getNotifier().notify();
                              } else {
                                    getNotifier().notify(message);
                              }
                        }
                  }

                  @Override
                  public void onCmdMessageReceived(List<EMMessage> messages) {
                        for (EMMessage message : messages) {
                              EMLog.d(TAG, "receive command message");
                              //get message body
                              EMCmdMessageBody cmdMsgBody = (EMCmdMessageBody) message.getBody();
                              final String action = cmdMsgBody.action();//获取自定义action
                              //获取扩展属性 此处省略
                              //maybe you need get extension of your message
                              //message.getStringAttribute("");
                              EMLog.d(TAG, String.format("Command：action:%s,message:%s", action, message.toString()));
                        }
                  }

                  @Override
                  public void onMessageRead(List<EMMessage> messages) {
                  }

                  @Override
                  public void onMessageDelivered(List<EMMessage> message) {
                  }

                  @Override
                  public void onMessageRecalled(List<EMMessage> messages) {
                        for (EMMessage msg : messages) {
                              if (msg.getChatType() == ChatType.GroupChat && EaseAtMessageHelper.get().isAtMeMsg(msg)) {
                                    EaseAtMessageHelper.get().removeAtMeGroup(msg.getTo());
                              }
                              EMMessage msgNotification = EMMessage.createReceiveMessage(Type.TXT);
                              EMTextMessageBody txtBody = new EMTextMessageBody(String.format(appContext.getString(R.string.msg_recall_by_user), msg.getFrom()));
                              msgNotification.addBody(txtBody);
                              msgNotification.setFrom(msg.getFrom());
                              msgNotification.setTo(msg.getTo());
                              msgNotification.setUnread(false);
                              msgNotification.setMsgTime(msg.getMsgTime());
                              msgNotification.setLocalTime(msg.getMsgTime());
                              msgNotification.setChatType(msg.getChatType());
                              msgNotification.setAttribute(Constant.MESSAGE_TYPE_RECALL, true);
                              msgNotification.setStatus(Status.SUCCESS);
                              EMClient.getInstance().chatManager().saveMessage(msgNotification);
                        }
                  }

                  @Override
                  public void onMessageChanged(EMMessage message, Object change) {
                        EMLog.d(TAG, "change:");
                        EMLog.d(TAG, "change:" + change);
                  }
            };

            EMClient.getInstance().chatManager().addMessageListener(messageListener);
      }

      /**
       * if ever logged in
       *
       * @return
       */
      public boolean isLoggedIn() {
            return EMClient.getInstance().isLoggedInBefore();
      }

      /**
       * logout
       *
       * @param unbindDeviceToken whether you need unbind your device token
       * @param callback          callback
       */
      public void logout(boolean unbindDeviceToken, final EMCallBack callback) {
            endCall();
            Log.d(TAG, "logout: " + unbindDeviceToken);
            EMClient.getInstance().logout(unbindDeviceToken, new EMCallBack() {

                  @Override
                  public void onSuccess() {
                        Log.d(TAG, "logout: onSuccess");
                        reset();
                        if (callback != null) {
                              callback.onSuccess();
                        }

                  }

                  @Override
                  public void onProgress(int progress, String status) {
                        if (callback != null) {
                              callback.onProgress(progress, status);
                        }
                  }

                  @Override
                  public void onError(int code, String error) {
                        Log.d(TAG, "logout: onSuccess");
                        reset();
                        if (callback != null) {
                              callback.onError(code, error);
                        }
                  }
            });
      }

      /**
       * get instance of EaseNotifier
       *
       * @return
       */
      public EaseNotifier getNotifier() {
            return easeUI.getNotifier();
      }

      public DemoModel getModel() {
            return (DemoModel) demoModel;
      }

      /**
       * update contact list
       *
       * @param aContactList
       */
      public void setContactList(Map<String, EaseUser> aContactList) {
            if (aContactList == null) {
                  if (contactList != null) {
                        contactList.clear();
                  }
                  return;
            }

            contactList = aContactList;
      }

      /**
       * save single contact
       */
      public void saveContact(EaseUser user) {
            contactList.put(user.getUsername(), user);
            demoModel.saveContact(user);
      }

      /**
       * get contact list
       *
       * @return
       */
      public Map<String, EaseUser> getContactList() {
            if (isLoggedIn() && contactList == null) {
                  contactList = demoModel.getContactList();
            }

            // return a empty non-null object to avoid app crash
            if (contactList == null) {
                  return new Hashtable<String, EaseUser>();
            }

            return contactList;
      }

      /**
       * set current username
       *
       * @param username
       */
      public void setCurrentUserName(String username) {
            this.username = username;
            demoModel.setCurrentUserName(username);
      }

      /**
       * get current user's id
       */
      public String getCurrentUsernName() {
            if (username == null) {
                  username = demoModel.getCurrentUsernName();
            }
            return username;
      }

      public void setRobotList(Map<String, RobotUser> robotList) {
            this.robotList = robotList;
      }

      public Map<String, RobotUser> getRobotList() {
            if (isLoggedIn() && robotList == null) {
                  robotList = demoModel.getRobotList();
            }
            return robotList;
      }

      /**
       * update user list to cache and database
       *
       * @param contactInfoList
       */
      public void updateContactList(List<EaseUser> contactInfoList) {
            for (EaseUser u : contactInfoList) {
                  contactList.put(u.getUsername(), u);
            }
            ArrayList<EaseUser> mList = new ArrayList<EaseUser>();
            mList.addAll(contactList.values());
            demoModel.saveContactList(mList);
      }

      public UserProfileManager getUserProfileManager() {
            if (userProManager == null) {
                  userProManager = new UserProfileManager();
            }
            return userProManager;
      }

      void endCall() {
            try {
                  EMClient.getInstance().callManager().endCall();
            } catch (Exception e) {
                  e.printStackTrace();
            }
      }

      public void addSyncGroupListener(DataSyncListener listener) {
            if (listener == null) {
                  return;
            }
            if (!syncGroupsListeners.contains(listener)) {
                  syncGroupsListeners.add(listener);
            }
      }

      public void removeSyncGroupListener(DataSyncListener listener) {
            if (listener == null) {
                  return;
            }
            if (syncGroupsListeners.contains(listener)) {
                  syncGroupsListeners.remove(listener);
            }
      }

      public void addSyncContactListener(DataSyncListener listener) {
            if (listener == null) {
                  return;
            }
            if (!syncContactsListeners.contains(listener)) {
                  syncContactsListeners.add(listener);
            }
      }

      public void removeSyncContactListener(DataSyncListener listener) {
            if (listener == null) {
                  return;
            }
            if (syncContactsListeners.contains(listener)) {
                  syncContactsListeners.remove(listener);
            }
      }

      public void addSyncBlackListListener(DataSyncListener listener) {
            if (listener == null) {
                  return;
            }
            if (!syncBlackListListeners.contains(listener)) {
                  syncBlackListListeners.add(listener);
            }
      }

      public void removeSyncBlackListListener(DataSyncListener listener) {
            if (listener == null) {
                  return;
            }
            if (syncBlackListListeners.contains(listener)) {
                  syncBlackListListeners.remove(listener);
            }
      }

      /**
       * Get group list from server
       * This method will save the sync state
       *
       * @throws HyphenateException
       */
      public synchronized void asyncFetchGroupsFromServer(final EMCallBack callback) {
            if (isSyncingGroupsWithServer) {
                  return;
            }

            isSyncingGroupsWithServer = true;

            new Thread() {
                  @Override
                  public void run() {
                        try {
                              List<EMGroup> groups = EMClient.getInstance().groupManager().getJoinedGroupsFromServer();

                              // in case that logout already before server returns, we should return immediately
                              if (!isLoggedIn()) {
                                    isGroupsSyncedWithServer = false;
                                    isSyncingGroupsWithServer = false;
                                    noitifyGroupSyncListeners(false);
                                    return;
                              }

                              demoModel.setGroupsSynced(true);

                              isGroupsSyncedWithServer = true;
                              isSyncingGroupsWithServer = false;

                              //notify sync group list success
                              noitifyGroupSyncListeners(true);

                              if (callback != null) {
                                    callback.onSuccess();
                              }
                        } catch (HyphenateException e) {
                              demoModel.setGroupsSynced(false);
                              isGroupsSyncedWithServer = false;
                              isSyncingGroupsWithServer = false;
                              noitifyGroupSyncListeners(false);
                              if (callback != null) {
                                    callback.onError(e.getErrorCode(), e.toString());
                              }
                        }

                  }
            }.start();
      }

      public void noitifyGroupSyncListeners(boolean success) {
            for (DataSyncListener listener : syncGroupsListeners) {
                  listener.onSyncComplete(success);
            }
      }

      public void asyncFetchContactsFromServer(final EMValueCallBack<List<String>> callback) {
            if (isSyncingContactsWithServer) {
                  return;
            }

            isSyncingContactsWithServer = true;

            new Thread() {
                  @Override
                  public void run() {
                        List<String> usernames = null;
                        List<String> selfIds = null;
                        try {
                              usernames = EMClient.getInstance().contactManager().getAllContactsFromServer();
                              selfIds = EMClient.getInstance().contactManager().getSelfIdsOnOtherPlatform();
                              // in case that logout already before server returns, we should return immediately
                              if (!isLoggedIn()) {
                                    isContactsSyncedWithServer = false;
                                    isSyncingContactsWithServer = false;
                                    notifyContactsSyncListener(false);
                                    return;
                              }
                              if (selfIds.size() > 0) {
                                    usernames.addAll(selfIds);
                              }
                              Map<String, EaseUser> userlist = new HashMap<String, EaseUser>();
                              for (String username : usernames) {
                                    EaseUser user = new EaseUser(username);
                                    EaseCommonUtils.setUserInitialLetter(user);
                                    userlist.put(username, user);
                              }
                              // save the contact list to cache
                              getContactList().clear();
                              getContactList().putAll(userlist);
                              // save the contact list to database
                              UserDao dao = new UserDao(appContext);
                              List<EaseUser> users = new ArrayList<EaseUser>(userlist.values());
                              dao.saveContactList(users);

                              demoModel.setContactSynced(true);
                              EMLog.d(TAG, "set contact syn status to true");

                              isContactsSyncedWithServer = true;
                              isSyncingContactsWithServer = false;

                              //notify sync success
                              notifyContactsSyncListener(true);

                              getUserProfileManager().asyncFetchContactInfosFromServer(usernames, new EMValueCallBack<List<EaseUser>>() {

                                    @Override
                                    public void onSuccess(List<EaseUser> uList) {
                                          updateContactList(uList);
                                          getUserProfileManager();
                                    }

                                    @Override
                                    public void onError(int error, String errorMsg) {
                                    }
                              });
                              if (callback != null) {
                                    callback.onSuccess(usernames);
                              }
                        } catch (HyphenateException e) {
                              demoModel.setContactSynced(false);
                              isContactsSyncedWithServer = false;
                              isSyncingContactsWithServer = false;
                              notifyContactsSyncListener(false);
                              e.printStackTrace();
                              if (callback != null) {
                                    callback.onError(e.getErrorCode(), e.toString());
                              }
                        }

                  }
            }.start();
      }

      public void notifyContactsSyncListener(boolean success) {
            for (DataSyncListener listener : syncContactsListeners) {
                  listener.onSyncComplete(success);
            }
      }

      public void asyncFetchBlackListFromServer(final EMValueCallBack<List<String>> callback) {

            if (isSyncingBlackListWithServer) {
                  return;
            }

            isSyncingBlackListWithServer = true;

            new Thread() {
                  @Override
                  public void run() {
                        try {
                              List<String> usernames = EMClient.getInstance().contactManager().getBlackListFromServer();

                              // in case that logout already before server returns, we should return immediately
                              if (!isLoggedIn()) {
                                    isBlackListSyncedWithServer = false;
                                    isSyncingBlackListWithServer = false;
                                    notifyBlackListSyncListener(false);
                                    return;
                              }

                              demoModel.setBlacklistSynced(true);

                              isBlackListSyncedWithServer = true;
                              isSyncingBlackListWithServer = false;

                              notifyBlackListSyncListener(true);
                              if (callback != null) {
                                    callback.onSuccess(usernames);
                              }
                        } catch (HyphenateException e) {
                              demoModel.setBlacklistSynced(false);

                              isBlackListSyncedWithServer = false;
                              isSyncingBlackListWithServer = true;
                              e.printStackTrace();

                              if (callback != null) {
                                    callback.onError(e.getErrorCode(), e.toString());
                              }
                        }

                  }
            }.start();
      }

      public void notifyBlackListSyncListener(boolean success) {
            for (DataSyncListener listener : syncBlackListListeners) {
                  listener.onSyncComplete(success);
            }
      }

      public boolean isSyncingGroupsWithServer() {
            return isSyncingGroupsWithServer;
      }

      public boolean isSyncingContactsWithServer() {
            return isSyncingContactsWithServer;
      }

      public boolean isSyncingBlackListWithServer() {
            return isSyncingBlackListWithServer;
      }

      public boolean isGroupsSyncedWithServer() {
            return isGroupsSyncedWithServer;
      }

      public boolean isContactsSyncedWithServer() {
            return isContactsSyncedWithServer;
      }

      public boolean isBlackListSyncedWithServer() {
            return isBlackListSyncedWithServer;
      }

      synchronized void reset() {
            isSyncingGroupsWithServer = false;
            isSyncingContactsWithServer = false;
            isSyncingBlackListWithServer = false;

            demoModel.setGroupsSynced(false);
            demoModel.setContactSynced(false);
            demoModel.setBlacklistSynced(false);

            isGroupsSyncedWithServer = false;
            isContactsSyncedWithServer = false;
            isBlackListSyncedWithServer = false;

            isGroupAndContactListenerRegisted = false;

            setContactList(null);
            setRobotList(null);
            getUserProfileManager().reset();
            DemoDBManager.getInstance().closeDB();
      }

      public void pushActivity(Activity activity) {
            easeUI.pushActivity(activity);
      }

      public void popActivity(Activity activity) {
            easeUI.popActivity(activity);
      }

}
